#pragma once

#include "base.hpp"
#include "cli.hpp"
#include <map>

// 命令相关的
extern CommitMessage get_commit_message(std::string commitOid);

class InitCommand : public Command {
  public:
    InitCommand() : Command(CMD_INIT) {}
    ErrorMessage handle_command() override;
};

class HashObjectCommand : public Command {
  public:
    HashObjectCommand(std::string filename) : Command(CMD_HASH_OBJECT) {
        this->options["file-name"] = filename;
    }
    ErrorMessage handle_command() override;
};

class CatFileCommand : public Command {
  public:
    CatFileCommand(std::string object_id) : Command(CMD_CAT_FILE) {
        this->options["object-id"] = object_id;
    }
    ErrorMessage handle_command() override;
};

class WriteTreeCommand : public Command {
  public:
    WriteTreeCommand() : Command(CMD_WRITE_TREE) {}
    ErrorMessage handle_command() override;
};

class ReadTreeCommand : public Command {
  public:
    ReadTreeCommand(std::string oid) : Command(CMD_READ_TREE) { this->options["object-id"] = oid; }

    ErrorMessage handle_command() override;
};

class CommitCommand : public Command {
  public:
    CommitCommand(std::string commit_message) : Command(CMD_COMMIT) {
        this->options["commit-message"] = commit_message;
    }
    ErrorMessage handle_command() override;

  private:
    std::string tree_oid;
    ErrorMessage create_object_by_index();
};

class LogCommand : public Command {
  public:
    LogCommand() : Command(CMD_LOG){};
    LogCommand(std::string tag_name) : Command(CMD_LOG) { this->options["tag-name"] = tag_name; }

    ErrorMessage handle_command() override;

  private:
    ErrorMessage print_log_message(std::string oid);
};

enum CheckoutType {
    CKECKOUT_OID,
    CKECKOUT_TAG,
    CKECKOUT_BRANCH,
};

class CheckoutCommand : public Command {

  public:
    CheckoutCommand(CheckoutType type, std::string argv) : Command(CMD_CHECKOUT) {

        this->checkout_type = type;

        switch (type) {
        case CKECKOUT_OID:
            this->options["object-id"] = argv;
            break;
        case CKECKOUT_TAG:
            this->options["tag-name"] = argv;
            break;
        case CKECKOUT_BRANCH:
            this->options["branch-name"] = argv;
            break;
        default:
            std::cerr << "Invalid checkout type" << std::endl;
            exit(1);
        }
    };
    ErrorMessage handle_command() override;

  private:
    CheckoutType checkout_type;
    ErrorMessage checkout_by_oid(std::string oid);
    ErrorMessage checkout_by_tag(std::string tag_name);
    ErrorMessage checkout_by_branch(std::string branch_name);
};

class TagCommand : public Command {
  public:
    TagCommand(std::string tag_name, std::string commit_oid) : Command(CMD_TAG) {
        this->options["tag-name"] = tag_name;
        this->options["tag-oid"]  = commit_oid;
    }
    ErrorMessage handle_command() override;
};

enum BranchType {
    BRANCH_CREATE,
    BRANCH_LIST,
};

class BranchCommand : public Command {
  public:
    BranchCommand(BranchType type) : Command(CMD_BRANCH), branch_type(type) {}
    BranchCommand(BranchType type, std::string branch_name)
        : Command(CMD_BRANCH), branch_type(type) {
        this->options["branch-name"] = branch_name;
    }
    ErrorMessage handle_command() override;

  private:
    BranchType branch_type;
    ErrorMessage create_branch(std::string branch_name);
    ErrorMessage list_branch();
};

class StatusCommand : public Command {
  public:
    StatusCommand() : Command(CMD_STATUS) {}
    ErrorMessage handle_command() override;
};

class ResetCommand : public Command {
  public:
    ResetCommand(std::string reset_oid) : Command(CMD_RESET) {
        this->options["reset-oid"] = reset_oid;
    }
    ErrorMessage handle_command() override;
};

class ShowCommand : public Command {
  public:
    ShowCommand() : Command(CMD_SHOW) {}
    ErrorMessage handle_command() override;
};

enum DiffTypeCommand {
    DIFF_HEAD,
    DIFF_STAGED,
};

class DiffCommand : public Command {
  public:
    DiffCommand(DiffTypeCommand type) : Command(CMD_DIFF), diff_type(type) {}
    ErrorMessage handle_command() override;

  private:
    DiffTypeCommand diff_type;
};

class MergeCommand : public Command {
  public:
    MergeCommand(std::string branch_name) : Command(CMD_MERGE) {
        this->options["branch-name"] = branch_name;
    }
    ErrorMessage handle_command() override;
};

class MergeBaseCommand : public Command {
  public:
    MergeBaseCommand(std::string commit_oid1, std::string commit_oid2) : Command(CMD_MERGE) {
        this->options["branch-name-one"] = commit_oid1;
        this->options["branch-name-two"] = commit_oid2;
    }
    ErrorMessage handle_command() override;
};

class AddCommand : public Command {
  public:
    AddCommand(std::string filename) : Command(CMD_ADD) { this->options["file-name"] = filename; }
    ErrorMessage handle_command() override;
};